/**
 * @文件名称 ReflectTest.java  版本号：1.0
 * @创建日期 2018年9月12日 
 * @创建作者 me@longlonggo.com<br/>
 *
 */
package review.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 说明：
 * java反射实例
 * @version 1.0
 * @author me@longlonggo.com
 *
 */
public class ReflectTest {

    public static void main(String[] args) {
        /************************【1】获取类的Class对象**********************/
        System.out.println("************************【1】获取类的Class对象**********************");
        //1、使用对象的getClass方法，获取类型
        Boolean var1 = true;
        Class<?> classType1 = var1.getClass();
        System.out.println(classType1);  //class java.lang.Boolean
        
        //2、直接运用.class 语法
        Class<?> classType2 = Boolean.class;
        System.out.println(classType2); //class java.lang.Boolean
        
        //3、运用static method Class.forName()
        //该方法执行需进行异常处理
        try {
            Class<?> classType3 = Class.forName("java.lang.Boolean");
            System.out.println(classType3); //class java.lang.Boolean
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        //4、运用primitive wrapper classes的TYPE 语法
        //这里返回的是原生类型，和Boolean.class返回的不同
        Class<?> classType4 = Boolean.TYPE;
        System.out.println(classType4);     //boolean
        
        
        /************************【2】获取类的Fields变量名**********************/
        System.out.println("\n\n************************【2】获取类的Fields变量名**********************");
        Class<?> classExtendType = ExtendType.class;
        
        //1、使用getFields()方法获取类属性
        //只能输出public修饰的变量，包括父类
        Field[] fields = classExtendType.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }
        
        System.out.println("------------------------");
        
        //2、使用getDeclaredFields()方法获取属性
        //会输出所有类型变量，不含父类中定义的变量
        fields = classExtendType.getDeclaredFields();
        for (Field f : fields) {
            System.out.println(f);
        }
        

        /************************【3】获取类的Method方法**********************/
        System.out.println("\n\n************************【3】获取类的Method方法**********************");
        //1、使用getMethods()方法获取类方法
        //只能输出public修饰的方法，包括父类
        Method[] methods = classExtendType.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }
        System.out.println("------------------------");
        
        //2、使用getDeclaredMethods()方法获取
        //会输出所有方法，但不含父类中的方法，也不包含构造方法
        methods = classExtendType.getDeclaredMethods();
        for (Method m : methods) {
            System.out.println(m);
        }
        

        /********************【4】获取类的Constructor构造方法******************/
        System.out.println("\n\n********************【4】获取类的Constructor构造方法******************");
        Constructor<?>[] constructors = classExtendType.getConstructors();
        //1、getConstructors()获取公共构造方法
        //只能输出public修饰的方法，不能输出private,protected级别的构造方法，「且不包括父类构造方法」
        for (Constructor<?> c : constructors) {
            System.out.println(c);
        }
        
        System.out.println("------------------------");
        
        //2、getDeclaredConstructors()获取构造方法
        //将输出所有构造方法，包括private修饰的构造方法，但不包括父类构造方法
        constructors = classExtendType.getDeclaredConstructors();
        for (Constructor<?> c : constructors) {
            System.out.println(c);
        }
        
        
        /************************【5】通过反射创建新对象**********************/
        System.out.println("\n\n************************【5】通过反射创建新对象**********************");
        //1、使用无参构造器，创建对象
        //1.1、调用类Class对象的newInstance()方法，该方法会调用对象的默认构造器，没有默认构造则调用失败
        try {
            Object obj = classExtendType.newInstance();
            System.out.println(obj);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        
        System.out.println("------------------------");
        
        //1.2、调用构造器Constructor对象的newInstance()方法
        try {
            Constructor<?> constructor1 = classExtendType.getConstructor();
            Object obj = constructor1.newInstance();
            System.out.println(obj);
        } catch (NoSuchMethodException | SecurityException e) {
            // 无法找到方法或安全管理器验证不合法时抛出
            e.printStackTrace();
        } catch (InstantiationException e) {
            // 当应用程序试图使用类中的newInstance方法创建类的实例，但无法实例化指定的类对象时抛出
            // 出现原因：抽象类、接口、数组类、原始类型或void；或者类没有空构造函数时出现
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // 当应用程序试图反射性地创建实例(数组除外)、设置或获取字段或调用方法时抛出
            // 出现原因：调用了对象的私有private方法或变量
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // 参数传入非法异常
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // 检查性异常，Method.invoke(obj, args...)方法抛出。
            // 当被调用的方法的内部抛出了异常而没有被捕获时
            e.printStackTrace();
        }
        
        //2、使用有参构造器，创建对象
        try {
            //该处调用错误，应该使用getDeclaredConstructor()
            //因为ExtendType类的构造方法：ExtendType(int arg1, String arg2)没有定义为public，所以在执行实例化时将报异常
            //java.lang.NoSuchMethodException: review.reflect.ExtendType.<init>(int, java.lang.String)
//!            Constructor<?> constructor = classExtendType.getConstructor(int.class, String.class);
//!            Object obj = constructor.newInstance(1, "input String");
//!            System.out.println(obj);
            
            //以下为正确构造方式
            Constructor<?> constructor = classExtendType.getDeclaredConstructor(int.class, String.class);
            Object obj = constructor.newInstance(1, "input String");
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        
        /********************【6】使用反射调用类的方法******************/
        System.out.println("\n\n********************【6】使用反射调用类的方法******************");
        try {
            //1、用【5】创造的对象，直接调用自身方法，但是不能调用该对象的私有方法
            Constructor<?> constructor = classExtendType.getConstructor();
            Object obj = constructor.newInstance();
            ExtendType type = (ExtendType) obj;
            type.say("i'm longlonggo!!");
            
            //2、用【3】中获取类方法的形式,调用Field的Invoke方法调用函数
            //如果不设置setAccessible(true)，会报没有权限调用private的错误，如下：
            //java.lang.IllegalAccessException: Class review.reflect.ReflectTest can not access a member of class review.reflect.ExtendType with modifiers "private"
            Method logMethod = classExtendType.getDeclaredMethod("Log", String.class);
            logMethod.setAccessible(true);
            logMethod.invoke(obj, "i'm longlonggo!!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        

        /********************【7】使用反射设置/获取类的属性值******************/
        System.out.println("\n\n********************【7】使用反射设置/获取类的属性值******************");
        try {
            //1、用【5】创造的对象，直接进行get/set值，但是不能调用私有变量
            Constructor<?> constructor = classExtendType.getConstructor();
            Object obj = constructor.newInstance();
            ExtendType type = (ExtendType) obj;
            type.setPrvIntExtendField(888);
            System.out.println(type.getPrvIntExtendField());
            
            //2、用【2】中获取Field的形式
            // 注意因为set的变量为私有变量，必须用getDeclaredField()，否则会报找不到该字段异常
            // 同时，如果不设置setAccessible(true)，会报没有权限调用private的错误，如下：
            Field field = classExtendType.getDeclaredField("prvIntExtendField");
            field.setAccessible(true);
            field.setInt(obj, 1024);
            System.out.println(field.getInt(obj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
